Applying Lags to determine the best fit
#Automated ARIMA MODEL ( Applying Lags to determine the best fit.)
#
automated_arima_dth <- function (lag_value) {
#Data Preparation---------------------------------------------------------------
#For forecasting, we chose the latest data
trend_death_hb <- trend_hb_daily %>%
filter (hb_name == "Scotland") %>%
filter(date >="2021-06-01") %>%
filter(!(is.na(daily_deaths))) %>%
select(date, daily_deaths)
# Convert it into a time series
daily_death_hb_zoo <- zoo(trend_death_hb$daily_deaths,
order.by=as.Date(trend_death_hb$date, format='%m/%d/%Y'))
# Convert it into a time series
daily_death_hb_timeseries <- timeSeries::as.timeSeries(daily_death_hb_zoo)
#Step 1 : Visualise the time series---------------------------------------------
original_series_death<-autoplot(daily_death_hb_timeseries, ts.colour = '#5ab4ac')+
xlab("Month") +
ylab("Patient died")+
#scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
ggtitle("Trend on Deaths") +
color_theme()
#Step 2 : Identification of model (Finding d)-----------------------------------
# Identify whether the time series is stationary / non stationary
# using ADF Augmented Dickey-Fuller test
adf_test_death <- adf.test(daily_death_hb_timeseries)
first_diff_death<- diff(daily_death_hb_timeseries)
adf_test1_death <- adf.test(na.omit(first_diff_death))
#Create a data frame to store the adf values
adf_data_death <- data.frame(Data = c("Original", "First-Ordered"),
Dickey_Fuller = c(adf_test_death$statistic, adf_test1_death$statistic),
p_value = c(adf_test_death$p.value,adf_test1_death$p.value))
adf_data_death
# First Order Difference
first_diff_death<- diff(daily_death_hb_timeseries)
p<- autoplot(first_diff_death, ts.colour = '#5ab4ac') +
xlab("Month") +
ylab("DEATH")+
# scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
ggtitle("First-Order Difference Series") +
color_theme()
#Step 3: Estimate the parameters (Finding p and q)-----------------------------
par(mfrow=c(2,2))
acf_death <- acf1(first_diff_death, col=2:7, lwd=4)
pacf_death <- acf1(first_diff_death, pacf = TRUE, col=2:7, lwd=4)
#Step 4 : Build the ARIMA model------------------------------------------------
#Automated method
if (lag_value == 0){
auto_arima_fit_death <- auto.arima(daily_death_hb_timeseries,
seasonal=FALSE,
stepwise=FALSE,
approximation=FALSE,
trace = TRUE )
}
else{
auto_arima_fit_death <- auto.arima(lag(daily_death_hb_timeseries,lag_value),
seasonal=FALSE,
stepwise=FALSE,
approximation=FALSE,
trace = TRUE
)
}
#Finding the co-efficient
coef_dth<-lmtest::coeftest(auto_arima_fit_death)
#Step 5: Check the diagnostics
res_dth <-checkresiduals(auto_arima_fit_death, theme = color_theme())
#Step 6: Plot the actual data and fitted data-----------------------------------
# Convert model and time series to dataframe for plotting
daily_death_hb_timeseries_data <- fortify(daily_death_hb_timeseries) %>%
clean_names() %>%
remove_rownames %>%
rename (date = index,
death = data)%>%
mutate(index = seq(1:nrow(daily_death_hb_timeseries)))
arima_fit_dth_resid <- ts(daily_death_hb_timeseries[1:nrow(daily_death_hb_timeseries)]) - resid(auto_arima_fit_death)
arima_fit_dth_data <- fortify(arima_fit_dth_resid) %>%
clean_names() %>%
mutate(data = round(data,2))
fit_existing_dth_data <- daily_death_hb_timeseries_data %>%
inner_join(arima_fit_dth_data, by = c("index"))
#plotting the series along with the fitted values
fit_existing_dth_plot <- fit_existing_dth_data %>%
mutate(date = as.Date(date)) %>%
ggplot()+
aes(x=date, y = death)+
geom_line(color ="#5ab4ac")+
geom_line(aes(x= date, y = data), colour = "red" )+
xlab("Month") +
ylab("Deaths reported")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
ggtitle("Fitting the ARIMA model with existing data") +
color_theme()
#Step 7: Forecast the Model ----------------------------------------------------
forecast_dth_model <- forecast(auto_arima_fit_death,level = c(80, 95), h = 15)
#Convert the model to dataframe for plotting
# Negative values of the CI interval are considered as 0
forecast_dth_model_data <- fortify(forecast_dth_model) %>%
clean_names() %>%
mutate(data = round(data,2),
fitted= round(fitted,2)) %>%
mutate (lo_80 = ifelse(lo_80 < 0,0,lo_80),
lo_95 = ifelse(lo_95 < 0,0,lo_95)
)
forecast_start_date <- as.Date(max(daily_death_hb_timeseries_data$date)+1)
forecast_end_date <- as.Date(forecast_start_date+14)
forecast_dth_data <- forecast_dth_model_data %>%
filter(!(is.na(point_forecast))) %>%
mutate(date = seq(forecast_start_date,forecast_end_date, by =1)) %>%
select(-data,-fitted, -index)
fitted_dth_data <- forecast_dth_model_data %>%
filter(!(is.na(data))) %>%
inner_join(daily_death_hb_timeseries_data, by = c("index")) %>%
mutate(date = as.Date(date)) %>%
select(date, data, fitted)
#Plotting the Vaccination series plus the forecast and 95% prediction intervals
return(list(forecast_dth_data, fit_existing_dth_data))
}
Auto ARIMA is called for different lags
list_0 <- automated_arima_dth(0)
forecast_dth_data_0 <- list_0[[1]]
fitted_dth_data_0 <- list_0[[2]]
list_1 <- automated_arima_dth(1)
forecast_dth_data_1 <- list_1[[1]]
fitted_dth_data_1 <- list_1[[2]]
list_2 <- automated_arima_dth(2)
forecast_dth_data_2 <- list_2[[1]]
fitted_dth_data_2 <- list_2[[2]]
list_3 <- automated_arima_dth(3)
forecast_dth_data_3 <- list_3[[1]]
fitted_dth_data_3 <- list_3[[2]]
Each model ARIMA is plotted separately
#Time series plots for the next 60 days according to best ARIMA models with 80%–95% CI.
fitted_dth_data_0$lab1 = "ARIMA(2,1,3)"
forecast_data_dth_plot_0 <- fitted_dth_data_0 %>%
mutate(date = as.Date(date)) %>%
ggplot()+
geom_line(aes(x= date, y = death), color = "#5ab4ac")+
geom_line(aes(x= date, y = data), colour = "red" )+
geom_line(aes(x= date, y =point_forecast), color ="blue", data = forecast_dth_data_0 )+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_80, ymax = hi_80),
data = forecast_dth_data_0, alpha = 0.3, fill = "green")+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_95, ymax = hi_95),
data = forecast_dth_data_0, alpha = 0.1)+
geom_vline(aes(xintercept=as.numeric(min(date))),color="#f1a340", linetype="dashed",data = forecast_dth_data_0)+
#ggtitle("ARIMA (1,0,4") +
xlab("Month") +
ylab("Death reported")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
color_theme()+
scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
facet_wrap(~lab1)
ggplotly(forecast_data_dth_plot_0)
#Time series plots for the next 60 days according to best ARIMA models with 80%–95% CI.
fitted_dth_data_1$lab1 = "ARIMA(2,1,3) lag = 1"
forecast_data_dth_plot_1 <- fitted_dth_data_1 %>%
mutate(date = as.Date(date)) %>%
ggplot()+
geom_line(aes(x= date, y = death), color = "#5ab4ac")+
geom_line(aes(x= date, y = data), colour = "red" )+
geom_line(aes(x= date, y =point_forecast), color ="blue", data = forecast_dth_data_1 )+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_80, ymax = hi_80),
data = forecast_dth_data_1, alpha = 0.3, fill = "green")+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_95, ymax = hi_95),
data = forecast_dth_data_1, alpha = 0.1)+
geom_vline(aes(xintercept=as.numeric(min(date))),color="#f1a340", linetype="dashed",data = forecast_dth_data_1)+
# ggtitle("2,1,3") +
xlab("Month") +
ylab("Death reported")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
color_theme()+
scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
facet_wrap(~lab1)
ggplotly(forecast_data_dth_plot_1)
#Time series plots for the next 60 days according to best ARIMA models with 80%–95% CI.
fitted_dth_data_2$lab1 = "ARIMA(5,1,0)"
forecast_data_dth_plot_2 <- fitted_dth_data_2 %>%
mutate(date = as.Date(date)) %>%
ggplot()+
geom_line(aes(x= date, y = death), color = "#5ab4ac")+
geom_line(aes(x= date, y = data), colour = "red" )+
geom_line(aes(x= date, y =point_forecast), color ="blue", data = forecast_dth_data_2 )+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_80, ymax = hi_80),
data = forecast_dth_data_1, alpha = 0.3, fill = "green")+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_95, ymax = hi_95),
data = forecast_dth_data_1, alpha = 0.1)+
geom_vline(aes(xintercept=as.numeric(min(date))),color="#f1a340", linetype="dashed",data = forecast_dth_data_2)+
# ggtitle("ARIMA (5,1,0)") +
xlab("Month") +
ylab("Death reported")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
color_theme()+
scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
facet_wrap(~lab1)
ggplotly(forecast_data_dth_plot_2)
#Time series plots for the next 60 days according to best ARIMA models with 80%–95% CI.
fitted_dth_data_3$lab1 = "ARIMA(5,1,0) with drift"
forecast_data_dth_plot_3 <- fitted_dth_data_3 %>%
mutate(date = as.Date(date)) %>%
ggplot()+
geom_line(aes(x= date, y = death), color = "#5ab4ac")+
geom_line(aes(x= date, y = data), colour = "red" )+
geom_line(aes(x= date, y =point_forecast), color ="blue", data = forecast_dth_data_3 )+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_80, ymax = hi_80),
data = forecast_dth_data_3, alpha = 0.3, fill = "green")+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_95, ymax = hi_95),
data = forecast_dth_data_3, alpha = 0.1)+
geom_vline(aes(xintercept=as.numeric(min(date))),color="#f1a340", linetype="dashed",data = forecast_dth_data_3)+
xlab("Month") +
ylab("Death reported")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
color_theme()+
scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
facet_wrap(~lab1)
ggplotly(forecast_data_dth_plot_3)
subplot(ggplotly(forecast_data_dth_plot_0),
ggplotly(forecast_data_dth_plot_1),
ggplotly(forecast_data_dth_plot_2),
ggplotly(forecast_data_dth_plot_3), nrows = 2,
shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE)
colors <- c("ARIMA (2,1,3) lag = 1" = "blue",
"ARIMA (5,1,0)" = "red",
"ARIMA (2,1,2)" = "orange",
"ARIMA (2,1,3)" = "black")
forecast_data_dth_plot_all <- fitted_dth_data %>%
ggplot()+
geom_line(aes(x= date, y = data), color = "#5ab4ac")+
# geom_line(aes(x= date, y = fitted), colour = "red" )+
geom_line(aes(x= date, y =point_forecast, color ="ARIMA (2,1,3)"),data = forecast_dth_data_0)+
geom_line(aes(x= date, y =point_forecast, color ="ARIMA (2,1,3) lag = 1"),data = forecast_dth_data_1)+
geom_line(aes(x= date, y =point_forecast, color ="ARIMA (5,1,0)"), data = forecast_dth_data_2)+
geom_line(aes(x= date, y =point_forecast, color ="ARIMA (5,1,0) with drift"), data = forecast_dth_data_3 )+
labs(color = "Model")+
scale_color_manual(values = colors)+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_80, ymax = hi_80),
data = forecast_dth_data, alpha = 0.3, fill = "green")+
geom_ribbon(aes(x = date, y = point_forecast, ymin = lo_95, ymax = hi_95),
data = forecast_dth_data, alpha = 0.1)+
geom_vline(aes(xintercept=as.numeric(max(date))),color="#f1a340", linetype="dashed",data = fitted_dth_data)+
ggtitle("Projection of new Deaths based on various models") +
xlab("Month") +
ylab("Death reported")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+
color_theme()+
scale_x_date(breaks = "1 month", date_labels = "%b - %y" )+
geom_text(data=annotation,
aes( x=x, y=y, label=label),
color="blue",
size=4 )
ggplotly(forecast_data_dth_plot_all)
LS0tDQp0aXRsZTogIkF1dG9tYXRlZCBBUklNQSB3aXRoIGEgbGFnIChmb3IgUHJlc2VudGF0aW9uIHB1cnBvc2UpIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgIEFwcGx5aW5nIExhZ3MgdG8gZGV0ZXJtaW5lIHRoZSBiZXN0IGZpdA0KDQpgYGB7cn0NCiMgQSBmdW5jdGlvbiBpcyBjcmVhdGVkIHRvIHBlcmZvcm0gdGhlIGF1dG9tYXRlZCBhcmltYSBmaXR0aW5nIGFuZCBmb3JlY2FzdCByZXBlYXRlZGx5IGZvciBkaWZmZXJlbnQgbGFncy4NCg0KYXV0b21hdGVkX2FyaW1hX2R0aCA8LSBmdW5jdGlvbiAobGFnX3ZhbHVlKSB7DQojRGF0YSBQcmVwYXJhdGlvbi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojRm9yIGZvcmVjYXN0aW5nLCB3ZSBjaG9zZSB0aGUgbGF0ZXN0IGRhdGENCnRyZW5kX2RlYXRoX2hiIDwtIHRyZW5kX2hiX2RhaWx5ICU+JSANCiAgZmlsdGVyIChoYl9uYW1lID09ICJTY290bGFuZCIpICU+JSANCiAgZmlsdGVyKGRhdGUgPj0iMjAyMS0wNi0wMSIpICU+JSANCiAgZmlsdGVyKCEoaXMubmEoZGFpbHlfZGVhdGhzKSkpICU+JSANCiAgc2VsZWN0KGRhdGUsIGRhaWx5X2RlYXRocykNCg0KIyBDb252ZXJ0IGl0IGludG8gYSB0aW1lIHNlcmllcw0KZGFpbHlfZGVhdGhfaGJfem9vIDwtIHpvbyh0cmVuZF9kZWF0aF9oYiRkYWlseV9kZWF0aHMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlci5ieT1hcy5EYXRlKHRyZW5kX2RlYXRoX2hiJGRhdGUsIGZvcm1hdD0nJW0vJWQvJVknKSkNCg0KIyBDb252ZXJ0IGl0IGludG8gYSB0aW1lIHNlcmllcw0KZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllcyA8LSAgdGltZVNlcmllczo6YXMudGltZVNlcmllcyhkYWlseV9kZWF0aF9oYl96b28pDQoNCiNTdGVwIDEgOiBWaXN1YWxpc2UgdGhlIHRpbWUgc2VyaWVzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCm9yaWdpbmFsX3Nlcmllc19kZWF0aDwtYXV0b3Bsb3QoZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllcywgdHMuY29sb3VyID0gJyM1YWI0YWMnKSsNCiAgeGxhYigiTW9udGgiKSArIA0KICB5bGFiKCJQYXRpZW50IGRpZWQiKSsNCiAgI3NjYWxlX3hfZGF0ZShicmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViIC0gJXkiICkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICBnZ3RpdGxlKCJUcmVuZCBvbiBEZWF0aHMiKSArDQogIGNvbG9yX3RoZW1lKCkNCg0KI1N0ZXAgMiA6IElkZW50aWZpY2F0aW9uIG9mIG1vZGVsIChGaW5kaW5nIGQpLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBJZGVudGlmeSB3aGV0aGVyIHRoZSB0aW1lIHNlcmllcyBpcyBzdGF0aW9uYXJ5IC8gbm9uIHN0YXRpb25hcnkNCiMgdXNpbmcgQURGIEF1Z21lbnRlZCBEaWNrZXktRnVsbGVyIHRlc3QgDQoNCmFkZl90ZXN0X2RlYXRoIDwtIGFkZi50ZXN0KGRhaWx5X2RlYXRoX2hiX3RpbWVzZXJpZXMpDQoNCmZpcnN0X2RpZmZfZGVhdGg8LSBkaWZmKGRhaWx5X2RlYXRoX2hiX3RpbWVzZXJpZXMpDQphZGZfdGVzdDFfZGVhdGggPC0gYWRmLnRlc3QobmEub21pdChmaXJzdF9kaWZmX2RlYXRoKSkNCg0KI0NyZWF0ZSBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIGFkZiB2YWx1ZXMNCmFkZl9kYXRhX2RlYXRoIDwtIGRhdGEuZnJhbWUoRGF0YSA9IGMoIk9yaWdpbmFsIiwgIkZpcnN0LU9yZGVyZWQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlja2V5X0Z1bGxlciA9IGMoYWRmX3Rlc3RfZGVhdGgkc3RhdGlzdGljLCBhZGZfdGVzdDFfZGVhdGgkc3RhdGlzdGljKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcF92YWx1ZSA9IGMoYWRmX3Rlc3RfZGVhdGgkcC52YWx1ZSxhZGZfdGVzdDFfZGVhdGgkcC52YWx1ZSkpDQphZGZfZGF0YV9kZWF0aA0KDQojIEZpcnN0IE9yZGVyIERpZmZlcmVuY2UNCg0KZmlyc3RfZGlmZl9kZWF0aDwtIGRpZmYoZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllcykNCnA8LSBhdXRvcGxvdChmaXJzdF9kaWZmX2RlYXRoLCB0cy5jb2xvdXIgPSAnIzVhYjRhYycpICsNCiAgeGxhYigiTW9udGgiKSArIA0KICB5bGFiKCJERUFUSCIpKw0KICAjIHNjYWxlX3hfZGF0ZShicmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViIC0gJXkiICkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICBnZ3RpdGxlKCJGaXJzdC1PcmRlciBEaWZmZXJlbmNlIFNlcmllcyIpICsNCiAgY29sb3JfdGhlbWUoKQ0KDQojU3RlcCAzOiBFc3RpbWF0ZSB0aGUgcGFyYW1ldGVycyAoRmluZGluZyBwIGFuZCBxKS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnBhcihtZnJvdz1jKDIsMikpDQphY2ZfZGVhdGggIDwtIGFjZjEoZmlyc3RfZGlmZl9kZWF0aCwgY29sPTI6NywgbHdkPTQpDQpwYWNmX2RlYXRoIDwtIGFjZjEoZmlyc3RfZGlmZl9kZWF0aCwgIHBhY2YgPSBUUlVFLCBjb2w9Mjo3LCBsd2Q9NCkNCg0KI1N0ZXAgNCA6IEJ1aWxkIHRoZSBBUklNQSBtb2RlbC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSANCiNBdXRvbWF0ZWQgbWV0aG9kDQoNCmlmIChsYWdfdmFsdWUgPT0gMCl7DQogIGF1dG9fYXJpbWFfZml0X2RlYXRoIDwtIGF1dG8uYXJpbWEoZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWw9RkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXB3aXNlPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHByb3hpbWF0aW9uPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFjZSA9IFRSVUUgICkNCn0NCmVsc2V7DQphdXRvX2FyaW1hX2ZpdF9kZWF0aCA8LSBhdXRvLmFyaW1hKGxhZyhkYWlseV9kZWF0aF9oYl90aW1lc2VyaWVzLGxhZ192YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwd2lzZT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBwcm94aW1hdGlvbj1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhY2UgPSBUUlVFDQopDQp9DQojRmluZGluZyB0aGUgY28tZWZmaWNpZW50DQpjb2VmX2R0aDwtbG10ZXN0Ojpjb2VmdGVzdChhdXRvX2FyaW1hX2ZpdF9kZWF0aCkNCg0KI1N0ZXAgNTogQ2hlY2sgdGhlIGRpYWdub3N0aWNzDQpyZXNfZHRoIDwtY2hlY2tyZXNpZHVhbHMoYXV0b19hcmltYV9maXRfZGVhdGgsIHRoZW1lID0gY29sb3JfdGhlbWUoKSkNCg0KI1N0ZXAgNjogUGxvdCB0aGUgYWN0dWFsIGRhdGEgYW5kIGZpdHRlZCBkYXRhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBDb252ZXJ0IG1vZGVsIGFuZCB0aW1lIHNlcmllcyB0byBkYXRhZnJhbWUgZm9yIHBsb3R0aW5nDQoNCmRhaWx5X2RlYXRoX2hiX3RpbWVzZXJpZXNfZGF0YSA8LSBmb3J0aWZ5KGRhaWx5X2RlYXRoX2hiX3RpbWVzZXJpZXMpICU+JSANCiAgY2xlYW5fbmFtZXMoKSAlPiUgDQogIHJlbW92ZV9yb3duYW1lcyAlPiUgDQogIHJlbmFtZSAoZGF0ZSA9IGluZGV4LA0KICAgICAgICAgIGRlYXRoID0gZGF0YSklPiUgDQogIG11dGF0ZShpbmRleCA9IHNlcSgxOm5yb3coZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllcykpKQ0KDQphcmltYV9maXRfZHRoX3Jlc2lkIDwtIHRzKGRhaWx5X2RlYXRoX2hiX3RpbWVzZXJpZXNbMTpucm93KGRhaWx5X2RlYXRoX2hiX3RpbWVzZXJpZXMpXSkgLSByZXNpZChhdXRvX2FyaW1hX2ZpdF9kZWF0aCkNCg0KYXJpbWFfZml0X2R0aF9kYXRhIDwtIGZvcnRpZnkoYXJpbWFfZml0X2R0aF9yZXNpZCkgJT4lIA0KICBjbGVhbl9uYW1lcygpICU+JSANCiAgbXV0YXRlKGRhdGEgPSByb3VuZChkYXRhLDIpKQ0KDQpmaXRfZXhpc3RpbmdfZHRoX2RhdGEgPC0gZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllc19kYXRhICU+JSANCiAgaW5uZXJfam9pbihhcmltYV9maXRfZHRoX2RhdGEsIGJ5ID0gYygiaW5kZXgiKSkNCg0KI3Bsb3R0aW5nIHRoZSBzZXJpZXMgYWxvbmcgd2l0aCB0aGUgZml0dGVkIHZhbHVlcw0KZml0X2V4aXN0aW5nX2R0aF9wbG90IDwtIGZpdF9leGlzdGluZ19kdGhfZGF0YSAlPiUgDQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlKSkgJT4lIA0KICBnZ3Bsb3QoKSsNCiAgYWVzKHg9ZGF0ZSwgeSA9IGRlYXRoKSsNCiAgZ2VvbV9saW5lKGNvbG9yID0iIzVhYjRhYyIpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPSBkYXRhKSwgY29sb3VyID0gInJlZCIgKSsNCiAgeGxhYigiTW9udGgiKSArIA0KICB5bGFiKCJEZWF0aHMgcmVwb3J0ZWQiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogIHNjYWxlX3hfZGF0ZShicmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViIC0gJXkiICkrDQogIGdndGl0bGUoIkZpdHRpbmcgdGhlIEFSSU1BIG1vZGVsIHdpdGggZXhpc3RpbmcgZGF0YSIpICsNCiAgY29sb3JfdGhlbWUoKQ0KDQojU3RlcCA3OiBGb3JlY2FzdCB0aGUgTW9kZWwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpmb3JlY2FzdF9kdGhfbW9kZWwgPC0gZm9yZWNhc3QoYXV0b19hcmltYV9maXRfZGVhdGgsbGV2ZWwgPSBjKDgwLCA5NSksIGggPSAxNSkgDQoNCiNDb252ZXJ0IHRoZSBtb2RlbCB0byBkYXRhZnJhbWUgZm9yIHBsb3R0aW5nDQoNCiMgTmVnYXRpdmUgdmFsdWVzIG9mIHRoZSBDSSBpbnRlcnZhbCBhcmUgY29uc2lkZXJlZCBhcyAwDQoNCmZvcmVjYXN0X2R0aF9tb2RlbF9kYXRhIDwtIGZvcnRpZnkoZm9yZWNhc3RfZHRoX21vZGVsKSAlPiUgDQogIGNsZWFuX25hbWVzKCkgJT4lIA0KICBtdXRhdGUoZGF0YSA9IHJvdW5kKGRhdGEsMiksDQogICAgICAgICBmaXR0ZWQ9IHJvdW5kKGZpdHRlZCwyKSkgICU+JSANCiAgbXV0YXRlIChsb184MCA9IGlmZWxzZShsb184MCA8IDAsMCxsb184MCksDQogICAgICAgICAgbG9fOTUgPSBpZmVsc2UobG9fOTUgPCAwLDAsbG9fOTUpDQogICkNCmZvcmVjYXN0X3N0YXJ0X2RhdGUgPC0gYXMuRGF0ZShtYXgoZGFpbHlfZGVhdGhfaGJfdGltZXNlcmllc19kYXRhJGRhdGUpKzEpDQpmb3JlY2FzdF9lbmRfZGF0ZSA8LSBhcy5EYXRlKGZvcmVjYXN0X3N0YXJ0X2RhdGUrMTQpDQoNCmZvcmVjYXN0X2R0aF9kYXRhIDwtIGZvcmVjYXN0X2R0aF9tb2RlbF9kYXRhICU+JSANCiAgZmlsdGVyKCEoaXMubmEocG9pbnRfZm9yZWNhc3QpKSkgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IHNlcShmb3JlY2FzdF9zdGFydF9kYXRlLGZvcmVjYXN0X2VuZF9kYXRlLCBieSA9MSkpICU+JSANCiAgc2VsZWN0KC1kYXRhLC1maXR0ZWQsIC1pbmRleCkgIA0KDQpmaXR0ZWRfZHRoX2RhdGEgPC0gZm9yZWNhc3RfZHRoX21vZGVsX2RhdGEgJT4lIA0KICBmaWx0ZXIoIShpcy5uYShkYXRhKSkpICU+JSANCiAgaW5uZXJfam9pbihkYWlseV9kZWF0aF9oYl90aW1lc2VyaWVzX2RhdGEsIGJ5ID0gYygiaW5kZXgiKSkgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSkpICU+JSANCiAgc2VsZWN0KGRhdGUsIGRhdGEsIGZpdHRlZCkNCiNQbG90dGluZyB0aGUgVmFjY2luYXRpb24gc2VyaWVzIHBsdXMgdGhlIGZvcmVjYXN0IGFuZCA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbHMNCnJldHVybihsaXN0KGZvcmVjYXN0X2R0aF9kYXRhLCBmaXRfZXhpc3RpbmdfZHRoX2RhdGEpKQ0KfQ0KYGBgDQoNCkF1dG8gQVJJTUEgaXMgY2FsbGVkIGZvciBkaWZmZXJlbnQgbGFncw0KDQpgYGB7cn0NCmxpc3RfMCA8LSBhdXRvbWF0ZWRfYXJpbWFfZHRoKDApDQpmb3JlY2FzdF9kdGhfZGF0YV8wIDwtIGxpc3RfMFtbMV1dDQogZml0dGVkX2R0aF9kYXRhXzAgPC0gbGlzdF8wW1syXV0NCg0KbGlzdF8xIDwtIGF1dG9tYXRlZF9hcmltYV9kdGgoMSkNCmZvcmVjYXN0X2R0aF9kYXRhXzEgIDwtIGxpc3RfMVtbMV1dDQogZml0dGVkX2R0aF9kYXRhXzEgPC0gbGlzdF8xW1syXV0NCg0KbGlzdF8yIDwtIGF1dG9tYXRlZF9hcmltYV9kdGgoMikNCmZvcmVjYXN0X2R0aF9kYXRhXzIgPC0gbGlzdF8yW1sxXV0NCiBmaXR0ZWRfZHRoX2RhdGFfMiA8LSBsaXN0XzJbWzJdXQ0KDQpsaXN0XzMgPC0gYXV0b21hdGVkX2FyaW1hX2R0aCgzKQ0KZm9yZWNhc3RfZHRoX2RhdGFfMyA8LSBsaXN0XzNbWzFdXQ0KIGZpdHRlZF9kdGhfZGF0YV8zIDwtIGxpc3RfM1tbMl1dDQpgYGANCg0KRWFjaCBtb2RlbCBBUklNQSBpcyBwbG90dGVkIHNlcGFyYXRlbHkNCg0KYGBge3J9DQojVGltZSBzZXJpZXMgcGxvdHMgZm9yIHRoZSBuZXh0IDE1IGRheXMgYWNjb3JkaW5nIHRvIGJlc3QgQVJJTUEgbW9kZWxzIHdpdGggODAl4oCTOTUlIENJLg0KZml0dGVkX2R0aF9kYXRhXzAkbGFiMSA9ICJBUklNQSgyLDEsMykiDQpmb3JlY2FzdF9kYXRhX2R0aF9wbG90XzAgPC0gZml0dGVkX2R0aF9kYXRhXzAgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSkpICU+JSANCiAgZ2dwbG90KCkrDQogIGdlb21fbGluZShhZXMoeD0gZGF0ZSwgeSA9IGRlYXRoKSwgY29sb3IgPSAiIzVhYjRhYyIpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPSBkYXRhKSwgY29sb3VyID0gInJlZCIgKSsNCiAgZ2VvbV9saW5lKGFlcyh4PSBkYXRlLCB5ID1wb2ludF9mb3JlY2FzdCksIGNvbG9yID0iYmx1ZSIsIGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YV8wICkrDQogIGdlb21fcmliYm9uKGFlcyh4ID0gZGF0ZSwgeSA9IHBvaW50X2ZvcmVjYXN0LCB5bWluID0gbG9fODAsIHltYXggPSBoaV84MCksIA0KICAgICAgICAgICAgICBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMCwgYWxwaGEgPSAwLjMsIGZpbGwgPSAiZ3JlZW4iKSsNCiAgZ2VvbV9yaWJib24oYWVzKHggPSBkYXRlLCB5ID0gcG9pbnRfZm9yZWNhc3QsIHltaW4gPSBsb185NSwgeW1heCA9IGhpXzk1KSwgDQogICAgICAgICAgICAgIGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YV8wLCBhbHBoYSA9IDAuMSkrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YXMubnVtZXJpYyhtaW4oZGF0ZSkpKSxjb2xvcj0iI2YxYTM0MCIsIGxpbmV0eXBlPSJkYXNoZWQiLGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YV8wKSsNCiAgI2dndGl0bGUoIkFSSU1BICgxLDAsNCIpICsNCiAgeGxhYigiTW9udGgiKSArIA0KICB5bGFiKCJEZWF0aCByZXBvcnRlZCIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgY29sb3JfdGhlbWUoKSsNCiAgc2NhbGVfeF9kYXRlKGJyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIgLSAleSIgKSsNCiAgZmFjZXRfd3JhcCh+bGFiMSkNCmdncGxvdGx5KGZvcmVjYXN0X2RhdGFfZHRoX3Bsb3RfMCkNCmBgYA0KDQpgYGB7cn0NCiNUaW1lIHNlcmllcyBwbG90cyBmb3IgdGhlIG5leHQgNjAgZGF5cyBhY2NvcmRpbmcgdG8gYmVzdCBBUklNQSBtb2RlbHMgd2l0aCA4MCXigJM5NSUgQ0kuDQpmaXR0ZWRfZHRoX2RhdGFfMSRsYWIxID0gIkFSSU1BKDIsMSwzKSBsYWcgPSAxIg0KZm9yZWNhc3RfZGF0YV9kdGhfcGxvdF8xIDwtIGZpdHRlZF9kdGhfZGF0YV8xICU+JSANCiAgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKGRhdGUpKSAlPiUgDQogIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPSBkZWF0aCksIGNvbG9yID0gIiM1YWI0YWMiKSsNCiAgZ2VvbV9saW5lKGFlcyh4PSBkYXRlLCB5ID0gZGF0YSksIGNvbG91ciA9ICJyZWQiICkrDQogIGdlb21fbGluZShhZXMoeD0gZGF0ZSwgeSA9cG9pbnRfZm9yZWNhc3QpLCBjb2xvciA9ImJsdWUiLCBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMSApKw0KICBnZW9tX3JpYmJvbihhZXMoeCA9IGRhdGUsIHkgPSBwb2ludF9mb3JlY2FzdCwgeW1pbiA9IGxvXzgwLCB5bWF4ID0gaGlfODApLCANCiAgICAgICAgICAgICAgZGF0YSA9IGZvcmVjYXN0X2R0aF9kYXRhXzEsIGFscGhhID0gMC4zLCBmaWxsID0gImdyZWVuIikrDQogIGdlb21fcmliYm9uKGFlcyh4ID0gZGF0ZSwgeSA9IHBvaW50X2ZvcmVjYXN0LCB5bWluID0gbG9fOTUsIHltYXggPSBoaV85NSksIA0KICAgICAgICAgICAgICBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMSwgYWxwaGEgPSAwLjEpKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFzLm51bWVyaWMobWluKGRhdGUpKSksY29sb3I9IiNmMWEzNDAiLCBsaW5ldHlwZT0iZGFzaGVkIixkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMSkrDQogIyBnZ3RpdGxlKCIyLDEsMyIpICsNCiAgeGxhYigiTW9udGgiKSArIA0KICB5bGFiKCJEZWF0aCByZXBvcnRlZCIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgY29sb3JfdGhlbWUoKSsNCiAgc2NhbGVfeF9kYXRlKGJyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIgLSAleSIgKSsNCiAgZmFjZXRfd3JhcCh+bGFiMSkNCg0KZ2dwbG90bHkoZm9yZWNhc3RfZGF0YV9kdGhfcGxvdF8xKQ0KYGBgDQoNCmBgYHtyfQ0KI1RpbWUgc2VyaWVzIHBsb3RzIGZvciB0aGUgbmV4dCA2MCBkYXlzIGFjY29yZGluZyB0byBiZXN0IEFSSU1BIG1vZGVscyB3aXRoIDgwJeKAkzk1JSBDSS4NCmZpdHRlZF9kdGhfZGF0YV8yJGxhYjEgPSAiQVJJTUEoNSwxLDApIg0KZm9yZWNhc3RfZGF0YV9kdGhfcGxvdF8yIDwtIGZpdHRlZF9kdGhfZGF0YV8yICU+JSANCiAgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKGRhdGUpKSAlPiUgDQogIGdncGxvdCgpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPSBkZWF0aCksIGNvbG9yID0gIiM1YWI0YWMiKSsNCiAgZ2VvbV9saW5lKGFlcyh4PSBkYXRlLCB5ID0gZGF0YSksIGNvbG91ciA9ICJyZWQiICkrDQogIGdlb21fbGluZShhZXMoeD0gZGF0ZSwgeSA9cG9pbnRfZm9yZWNhc3QpLCBjb2xvciA9ImJsdWUiLCBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMiApKw0KICBnZW9tX3JpYmJvbihhZXMoeCA9IGRhdGUsIHkgPSBwb2ludF9mb3JlY2FzdCwgeW1pbiA9IGxvXzgwLCB5bWF4ID0gaGlfODApLCANCiAgICAgICAgICAgICAgZGF0YSA9IGZvcmVjYXN0X2R0aF9kYXRhXzEsIGFscGhhID0gMC4zLCBmaWxsID0gImdyZWVuIikrDQogIGdlb21fcmliYm9uKGFlcyh4ID0gZGF0ZSwgeSA9IHBvaW50X2ZvcmVjYXN0LCB5bWluID0gbG9fOTUsIHltYXggPSBoaV85NSksIA0KICAgICAgICAgICAgICBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMSwgYWxwaGEgPSAwLjEpKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFzLm51bWVyaWMobWluKGRhdGUpKSksY29sb3I9IiNmMWEzNDAiLCBsaW5ldHlwZT0iZGFzaGVkIixkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMikrDQogIyBnZ3RpdGxlKCJBUklNQSAoNSwxLDApIikgKw0KICB4bGFiKCJNb250aCIpICsgDQogIHlsYWIoIkRlYXRoIHJlcG9ydGVkIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICBjb2xvcl90aGVtZSgpKw0KICBzY2FsZV94X2RhdGUoYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiAtICV5IiApKw0KICBmYWNldF93cmFwKH5sYWIxKQ0KZ2dwbG90bHkoZm9yZWNhc3RfZGF0YV9kdGhfcGxvdF8yKQ0KYGBgDQoNCmBgYHtyfQ0KI1RpbWUgc2VyaWVzIHBsb3RzIGZvciB0aGUgbmV4dCA2MCBkYXlzIGFjY29yZGluZyB0byBiZXN0IEFSSU1BIG1vZGVscyB3aXRoIDgwJeKAkzk1JSBDSS4NCmZpdHRlZF9kdGhfZGF0YV8zJGxhYjEgPSAiQVJJTUEoNSwxLDApIHdpdGggZHJpZnQiDQpmb3JlY2FzdF9kYXRhX2R0aF9wbG90XzMgPC0gZml0dGVkX2R0aF9kYXRhXzMgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSkpICU+JSANCiAgZ2dwbG90KCkrDQogIGdlb21fbGluZShhZXMoeD0gZGF0ZSwgeSA9IGRlYXRoKSwgY29sb3IgPSAiIzVhYjRhYyIpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPSBkYXRhKSwgY29sb3VyID0gInJlZCIgKSsNCiAgZ2VvbV9saW5lKGFlcyh4PSBkYXRlLCB5ID1wb2ludF9mb3JlY2FzdCksIGNvbG9yID0iYmx1ZSIsIGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YV8zICkrDQogIGdlb21fcmliYm9uKGFlcyh4ID0gZGF0ZSwgeSA9IHBvaW50X2ZvcmVjYXN0LCB5bWluID0gbG9fODAsIHltYXggPSBoaV84MCksIA0KICAgICAgICAgICAgICBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMywgYWxwaGEgPSAwLjMsIGZpbGwgPSAiZ3JlZW4iKSsNCiAgZ2VvbV9yaWJib24oYWVzKHggPSBkYXRlLCB5ID0gcG9pbnRfZm9yZWNhc3QsIHltaW4gPSBsb185NSwgeW1heCA9IGhpXzk1KSwgDQogICAgICAgICAgICAgIGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YV8zLCBhbHBoYSA9IDAuMSkrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YXMubnVtZXJpYyhtaW4oZGF0ZSkpKSxjb2xvcj0iI2YxYTM0MCIsIGxpbmV0eXBlPSJkYXNoZWQiLGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YV8zKSsNCiAgeGxhYigiTW9udGgiKSArIA0KICB5bGFiKCJEZWF0aCByZXBvcnRlZCIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgY29sb3JfdGhlbWUoKSsNCiAgc2NhbGVfeF9kYXRlKGJyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIgLSAleSIgKSsNCiAgZmFjZXRfd3JhcCh+bGFiMSkNCg0KZ2dwbG90bHkoZm9yZWNhc3RfZGF0YV9kdGhfcGxvdF8zKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VicGxvdChnZ3Bsb3RseShmb3JlY2FzdF9kYXRhX2R0aF9wbG90XzApLA0KICAgICAgICBnZ3Bsb3RseShmb3JlY2FzdF9kYXRhX2R0aF9wbG90XzEpLA0KICAgICAgICBnZ3Bsb3RseShmb3JlY2FzdF9kYXRhX2R0aF9wbG90XzIpLA0KICAgICAgICBnZ3Bsb3RseShmb3JlY2FzdF9kYXRhX2R0aF9wbG90XzMpLCBucm93cyA9IDIsDQogICAgICAgIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRSkNCmBgYA0KDQpgYGB7cn0NCmNvbG9ycyA8LSBjKCJBUklNQSAoMiwxLDMpIGxhZyA9IDEiID0gImJsdWUiLCANCiAgICAgICAgICAgICJBUklNQSAoNSwxLDApIiA9ICJyZWQiLCANCiAgICAgICAgICAgICJBUklNQSAoMiwxLDIpIiA9ICJvcmFuZ2UiLA0KICAgICAgICAgICAgIkFSSU1BICgyLDEsMykiID0gImJsYWNrIikNCg0KZm9yZWNhc3RfZGF0YV9kdGhfcGxvdF9hbGwgPC0gZml0dGVkX2R0aF9kYXRhICU+JSANCiAgZ2dwbG90KCkrDQogIGdlb21fbGluZShhZXMoeD0gZGF0ZSwgeSA9IGRhdGEpLCBjb2xvciA9ICIjNWFiNGFjIikrDQogIyBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPSBmaXR0ZWQpLCBjb2xvdXIgPSAicmVkIiApKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPXBvaW50X2ZvcmVjYXN0LCBjb2xvciA9IkFSSU1BICgyLDEsMykiKSxkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMCkrDQogIGdlb21fbGluZShhZXMoeD0gZGF0ZSwgeSA9cG9pbnRfZm9yZWNhc3QsIGNvbG9yID0iQVJJTUEgKDIsMSwzKSBsYWcgPSAxIiksZGF0YSA9IGZvcmVjYXN0X2R0aF9kYXRhXzEpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPXBvaW50X2ZvcmVjYXN0LCBjb2xvciA9IkFSSU1BICg1LDEsMCkiKSwgZGF0YSA9IGZvcmVjYXN0X2R0aF9kYXRhXzIpKw0KICBnZW9tX2xpbmUoYWVzKHg9IGRhdGUsIHkgPXBvaW50X2ZvcmVjYXN0LCBjb2xvciA9IkFSSU1BICg1LDEsMCkgd2l0aCBkcmlmdCIpLCBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGFfMyApKw0KICBsYWJzKGNvbG9yID0gIk1vZGVsIikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpKw0KICBnZW9tX3JpYmJvbihhZXMoeCA9IGRhdGUsIHkgPSBwb2ludF9mb3JlY2FzdCwgeW1pbiA9IGxvXzgwLCB5bWF4ID0gaGlfODApLA0KICAgICAgICAgICAgICBkYXRhID0gZm9yZWNhc3RfZHRoX2RhdGEsIGFscGhhID0gMC4zLCBmaWxsID0gImdyZWVuIikrDQogIGdlb21fcmliYm9uKGFlcyh4ID0gZGF0ZSwgeSA9IHBvaW50X2ZvcmVjYXN0LCB5bWluID0gbG9fOTUsIHltYXggPSBoaV85NSksDQogICAgICAgICAgICAgIGRhdGEgPSBmb3JlY2FzdF9kdGhfZGF0YSwgYWxwaGEgPSAwLjEpKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFzLm51bWVyaWMobWF4KGRhdGUpKSksY29sb3I9IiNmMWEzNDAiLCBsaW5ldHlwZT0iZGFzaGVkIixkYXRhID0gZml0dGVkX2R0aF9kYXRhKSsNCiAgZ2d0aXRsZSgiUHJvamVjdGlvbiBvZiBuZXcgRGVhdGhzIGJhc2VkIG9uIHZhcmlvdXMgbW9kZWxzIikgKw0KICB4bGFiKCJNb250aCIpICsgDQogIHlsYWIoIkRlYXRoIHJlcG9ydGVkIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICBjb2xvcl90aGVtZSgpKw0KICBzY2FsZV94X2RhdGUoYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiAtICV5IiApKw0KICBnZW9tX3RleHQoZGF0YT1hbm5vdGF0aW9uLCANCiAgICAgICAgICAgIGFlcyggeD14LCB5PXksIGxhYmVsPWxhYmVsKSwgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgIGNvbG9yPSJibHVlIiwgDQogICAgICAgICAgICBzaXplPTQgKQ0KDQpnZ3Bsb3RseShmb3JlY2FzdF9kYXRhX2R0aF9wbG90X2FsbCkNCmBgYA0K